The = Operator Function

I have a custom type based on a DxlObject. I wanted to write an Operator Function for '=' but can't get it to work. Seems like my Operator Function also gets called for DxlObjects, not just MyType. It all works ok if I use a different symbol ( replace '=>' with '=' in the code to get the error).

I guess it just konws that MyType is really just a DxlObject and replaces the DxlObjects Operator Function with mine. Is this just the way it is or am I doing something wrong?
 

struct MyType {}
 
// Base the MyType Struct on a DxlObject
DxlObject DxlObjectOf(MyType MyTypeObject)
{
    return (addr_ MyTypeObject) DxlObject
}
 
MyType createMyType()
{
        // MyType Struct based on a DxlObject
        DxlObject BaseObject = new()
        BaseObject->"SomeProperty" = "Initial"
        return (addr_ BaseObject) MyType
}
 
void deleteMyType(MyType &MyTypeObject)
{
        DxlObject BaseObject = (DxlObjectOf(MyTypeObject))
        delete BaseObject
}
 
void ::=> (MyType MyTypeObject, string NewValue)
{
        DxlObject BaseObject = (DxlObjectOf(MyTypeObject))
        BaseObject->"SomeProperty" = NewValue
}
 
string GetSomeProperty(MyType MyTypeObject)
{
        DxlObject BaseObject = (DxlObjectOf(MyTypeObject))
 
        return (BaseObject->"SomeProperty") string
}
 
MyType Test = createMyType()
print GetSomeProperty(Test) "\n"
 
Test => "New"
print GetSomeProperty(Test) "\n"
 
Test => "And Now?"
print GetSomeProperty(Test) "\n"
 
deleteMyType(Test)

SystemAdmin - Tue Jan 11 08:09:00 EST 2011

Re: The = Operator Function
Mathias Mamsch - Tue Jan 11 09:27:45 EST 2011

I consider this a bug in the parser, when it comes to determining the correct return value for DXL expressions. There are some bugs with symbol lookup in the DXL parser, for example that defining a int read(int) {} function will shadow ALL read(void) functions on the global level therefore breaking code, which for example uses the read permission.

This is another one. You CAN get it to work by telling DOORS the correct types (See below), but it will break all DxlObject->string code that comes after the declaration of the = operator. Therefore I would not recommend to use that ::= operator, except if you know in your library you don't want to use DxlObjects so much, or you replace the DxlObject->string = value calls.

It may be nice to know what is going on. DxlObject->string can be (ambigously) resolved into two symbols:
 

DxlObjectLHS ::-> (DxlObject obj, string fieldName)
_x           ::-> (DxlObject obj, string fieldName)

 


That alone is a sin for the DXL parser, since it cannot handle ambiguous return values correctly. So in your example it will choose the second one instead of the first one. And then it has to resolve the resulting ::= (_x, string). And since the _ types match on every type it will call the first matching ::= operator it will find, namely the one you just defined (which is on top of the symbol stack). So it will cast the DxlObjectLHS to DxlObject, which makes stuff crash.

Hope that makes some sense. Regards, Mathias

 

 

 

...
void ::= (MyType MyTypeObject, string NewValue)
{
    DxlObject BaseObject = (DxlObjectOf(MyTypeObject))
        ((BaseObject->"SomeProperty") DxlObjectLHS) = NewValue // Tell DOORS the right type
}
...

 

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

 

Re: The = Operator Function
SystemAdmin - Wed Jan 12 10:24:05 EST 2011

Mathias Mamsch - Tue Jan 11 09:27:45 EST 2011

I consider this a bug in the parser, when it comes to determining the correct return value for DXL expressions. There are some bugs with symbol lookup in the DXL parser, for example that defining a int read(int) {} function will shadow ALL read(void) functions on the global level therefore breaking code, which for example uses the read permission.

This is another one. You CAN get it to work by telling DOORS the correct types (See below), but it will break all DxlObject->string code that comes after the declaration of the = operator. Therefore I would not recommend to use that ::= operator, except if you know in your library you don't want to use DxlObjects so much, or you replace the DxlObject->string = value calls.

It may be nice to know what is going on. DxlObject->string can be (ambigously) resolved into two symbols:
 

DxlObjectLHS ::-> (DxlObject obj, string fieldName)
_x           ::-> (DxlObject obj, string fieldName)

 


That alone is a sin for the DXL parser, since it cannot handle ambiguous return values correctly. So in your example it will choose the second one instead of the first one. And then it has to resolve the resulting ::= (_x, string). And since the _ types match on every type it will call the first matching ::= operator it will find, namely the one you just defined (which is on top of the symbol stack). So it will cast the DxlObjectLHS to DxlObject, which makes stuff crash.

Hope that makes some sense. Regards, Mathias

 

 

 

...
void ::= (MyType MyTypeObject, string NewValue)
{
    DxlObject BaseObject = (DxlObjectOf(MyTypeObject))
        ((BaseObject->"SomeProperty") DxlObjectLHS) = NewValue // Tell DOORS the right type
}
...

 

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

 

Thanks for the explanation. I think I'll avoid the = operator.

I don't know if it is a related issue, but I have discovered something else with the addition of this code:

MyType ::. (MyType MyTypeObject, string Value)
{
    return MyTypeObject
}
 
MyType ::. (MyType MyTypeObject, int Value)
{
        return MyTypeObject
}
 
MyType Test = createMyType()
 
print GetSomeProperty(Test."One") "\n"
print GetSomeProperty(Test."One"."Two") "\n"
print GetSomeProperty(Test.1) "\n"
print GetSomeProperty(Test."One".1) "\n"
print GetSomeProperty(Test."One".2) "\n"
print GetSomeProperty(Test.1.2) "\n"
 
deleteMyType(Test)

 


"Test.1.2" is causing an "incorrect arguments for (.)" error.
It is ok so long as .int operations are only last in the chain.
All is well for chained strings though...

 

Re: The = Operator Function
SystemAdmin - Wed Jan 12 10:54:19 EST 2011

Mathias Mamsch - Tue Jan 11 09:27:45 EST 2011

I consider this a bug in the parser, when it comes to determining the correct return value for DXL expressions. There are some bugs with symbol lookup in the DXL parser, for example that defining a int read(int) {} function will shadow ALL read(void) functions on the global level therefore breaking code, which for example uses the read permission.

This is another one. You CAN get it to work by telling DOORS the correct types (See below), but it will break all DxlObject->string code that comes after the declaration of the = operator. Therefore I would not recommend to use that ::= operator, except if you know in your library you don't want to use DxlObjects so much, or you replace the DxlObject->string = value calls.

It may be nice to know what is going on. DxlObject->string can be (ambigously) resolved into two symbols:
 

DxlObjectLHS ::-> (DxlObject obj, string fieldName)
_x           ::-> (DxlObject obj, string fieldName)

 


That alone is a sin for the DXL parser, since it cannot handle ambiguous return values correctly. So in your example it will choose the second one instead of the first one. And then it has to resolve the resulting ::= (_x, string). And since the _ types match on every type it will call the first matching ::= operator it will find, namely the one you just defined (which is on top of the symbol stack). So it will cast the DxlObjectLHS to DxlObject, which makes stuff crash.

Hope that makes some sense. Regards, Mathias

 

 

 

...
void ::= (MyType MyTypeObject, string NewValue)
{
    DxlObject BaseObject = (DxlObjectOf(MyTypeObject))
        ((BaseObject->"SomeProperty") DxlObjectLHS) = NewValue // Tell DOORS the right type
}
...

 

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

 

As a hack/kludge is it possible to re-declare some offending DXL system function signatures in the "user-space" so the wild-card variation is the last choice?

I'm thinking that the DXL perm signatures are implicitly present (no #include file) and user-declared function signatures take precedence over DXL system signatures.

If a troublesome DXL system signature is causing problems (especially those "wild-card" _m types) I'm wondering if the troublesome DXL signature were re-declared in user-space in the desired proper order, would this fix the problem?

In general, if we had something like:
 

wild-card signature // or would this be last?
 
specific signature 1
specific signature 2
specific signature 3
 
...
 
specific signature N

 


Would this force DXL to choose a concise function (numbered 1..N) and only use the wild-card function if a concise (non _m) function is not found?

What are the general rules for choosing the functions of a given name considering:

 

 

 

  • implicit DXL vs. user-declared function
  • lexical order in file (e.g., which comes first or last)
  • special rules for wild-card types (e.g., _m) vs. specific types.
  • function return values vs. parameter list values.

 

 

 

COMMENT - There is a reason almost all popular programing languages allow function overloading based on parameters only (not the return type) -- because it is too buggy and hard otherwise.

 

Re: The = Operator Function
SystemAdmin - Wed Jan 12 11:48:12 EST 2011

SystemAdmin - Wed Jan 12 10:54:19 EST 2011

As a hack/kludge is it possible to re-declare some offending DXL system function signatures in the "user-space" so the wild-card variation is the last choice?

I'm thinking that the DXL perm signatures are implicitly present (no #include file) and user-declared function signatures take precedence over DXL system signatures.

If a troublesome DXL system signature is causing problems (especially those "wild-card" _m types) I'm wondering if the troublesome DXL signature were re-declared in user-space in the desired proper order, would this fix the problem?

In general, if we had something like:
 

wild-card signature // or would this be last?
 
specific signature 1
specific signature 2
specific signature 3
 
...
 
specific signature N

 


Would this force DXL to choose a concise function (numbered 1..N) and only use the wild-card function if a concise (non _m) function is not found?

What are the general rules for choosing the functions of a given name considering:

 

 

 

  • implicit DXL vs. user-declared function
  • lexical order in file (e.g., which comes first or last)
  • special rules for wild-card types (e.g., _m) vs. specific types.
  • function return values vs. parameter list values.

 

 

 

COMMENT - There is a reason almost all popular programing languages allow function overloading based on parameters only (not the return type) -- because it is too buggy and hard otherwise.

 

Actually, I think I'm being an idiot and it is interpreting int.int as a real.
Not sure why int.string wont work.
If I chang the operator symbol to '>' instead of '.' it seems to work fine.

Whilst trying different operators I noticed that some characters don't work eg. '_'.
Are there some rules about what is allowed?

Re: The = Operator Function
Mathias Mamsch - Wed Jan 12 16:04:42 EST 2011

SystemAdmin - Wed Jan 12 11:48:12 EST 2011
Actually, I think I'm being an idiot and it is interpreting int.int as a real.
Not sure why int.string wont work.
If I chang the operator symbol to '>' instead of '.' it seems to work fine.

Whilst trying different operators I noticed that some characters don't work eg. '_'.
Are there some rules about what is allowed?

The following works fine:
 

<costruct MyType {}
 
string GetSomeProperty (MyType) { return dxlHere[10:] "OK" }
MyType ::. (MyType MyTypeObject, string Value) { return MyTypeObject }
MyType ::. (MyType MyTypeObject, int Value) { return MyTypeObject }
 
MyType Test = addr_ (new DxlObject) 
 
print (GetSomeProperty(Test."One")) "\n"
print (GetSomeProperty(Test."One"."Two")) "\n"
print (GetSomeProperty(Test.1)) "\n"
print (GetSomeProperty(Test."One".1)) "\n"
print (GetSomeProperty(Test."One".2)) "\n"
print (GetSomeProperty(Test . 1 . 2)) "\n"
print (GetSomeProperty(Test . 2 . "One")) "\n"

 


The DXL expression evaluation mechanism is pretty complex to understand. DOORS uses yacc with the DXL grammar. Yacc precedence works as followed:

 

 

  • Each terminal (token in the source token stream, e.g. the operators) has a precedence and an associativity (left or right).
  • Grammar rules are evaluated in order of their precedence, which is defined by the right-most terminal of the grammar rule (guess why DXL is right associative?).


Lets have a look at a part of the DXL grammar(look at doors.exe to find it):

 

 

item : range
range : statement
range : expq
expq : exp
expq : exp QUEST(?) exp COLON(:) exp
exp : UMIN(-) exp
exp : PLUS(+) exp
exp : DOLL($) exp3
exp : LNEG(!) exp
exp : ASTX(*) K_CONST_o exp
exp : BAND(&) K_CONST_o exp
exp : exp FSL(/) exp
exp : exp EXP(^^) exp
exp : exp ASTX(*) exp
exp : exp BSL(\) exp
exp : exp REM(%) exp
exp : exp PLUS(+) exp
exp : exp UMIN(-) exp
exp : exp LSHFT(<<) exp
exp : exp RSHFT(>>) exp
exp : exp DIAM() exp
exp : exp MINI(<?) exp
exp : exp MAXI(>?) exp
exp : exp LT(<) exp
exp : exp GT(>) exp
exp : exp LTE(<=) exp
exp : exp GTE(>=) exp
exp : exp EQUS(==) exp
exp : exp NEQUS(!=) exp
exp : exp BAND(&) exp
exp : exp XOR(^) exp
exp : exp BOR(|) exp
exp : exp AND(&&) exp
exp : exp OR(||) exp
exp : exp ASSR(<-) exp
exp : exp ASSP(:=) exp
exp : exp GUARD(=>) exp
 
exp1 : name
exp1 : INT
exp1 : REAL
exp1 : CHARACTER
exp1 : STRING
exp1 : LB simpleType_o rrange_o_range_o RB
 
exp1a : exp1
exp1a : exp1a LSB bounds RSB
 
exp2 : exp1a
exp2 : exp2 SUB(->) exp1a
exp2 : exp2 LINKA(<->) exp1a
exp2 : exp2 DOT(.) exp1a
exp2 : exp2 AT(@) exp1a
exp2 : exp2 PLPL(++)
exp2 : exp2 MINMIN(--)
exp2 : PLPL exp2
exp2 : MINMIN exp2
exp2 : TILDA(~) exp2
exp2 : K_SIZE exp3
exp2 : K_DEFAULT COLON_o rblock
exp3 : exp1
exp3 : eargs
exp4 : exp2
exp4 : exp2 exp4
exp4 : exp2 simpleType
exp4 : exp4 CC exp2
exp : exp4



If I write a . b + C this will be tokenized to name(a), DOT, name(b), PLUS, name(c) . Yacc will take these input tokens, look on the next token and try to evaluate the above rules on it. When no rules apply then yacc will shift the next input token and try again to find a rule. So yacc will start with the input token "name(a)" and apply the following rule: "exp1 : name". So our "name" token becomes a "exp1" token. Yacc will evaluate further: name -> exp1 -> exp2, then it will find, there is a rule "exp2 . exp1a", so it will shift the next input token 'DOT'. Again there is no rule for "exp2 DOT" so take another input token. No we have "exp2 DOT name(b)". Since there is no rule for "exp2 DOT name" it will try to find rules for "name" and continue evaluating:

exp2 DOT name(b) -> exp2 DOT exp1 -> exp2 DOT exp1a -> boom exp2

Here we have found a rule: exp2 : exp2 DOT exp1a, that means the DOT is 'consumed' and therefore executed before the \+ is considered. Would there be no rule for "exp2 DOT something", then DXL would shift the next input token and try to evaluate that. So in this case I can see from the grammar that DXL will evaluate (a.b) + C, so in this case the . operator comes first. So therefore we can see the following.

The operators ->, <->, . ,@ , ++, --, ~ should take precedence over the other operators. Then should come the CC (concatenation) operator. Then comes the rest of the operators. The precedence for the other operators is unclear, we all know that for example * will take precedence over +. One would need to try this.
Lets come to the symbol lookup:

If you call a function in DXL other grammar rules (not listed) apply. First DXL has to differentiate between statements like int a and print a. For this it has internal symbol tables which tell it "print" is a function and "int" is a type and a is variable of type string (for example). Whenever you declare a variable or function it will be appended to the symbol table. Now due to polymorphism the parser needs to determine WHICH print function to call. To be exact every DXL context has its own symbol table. The internal perms are on the top level DXL context while your user program runs in its own context. So if your user program calls print (string) the DXL parser will first look in the local symbol table for a "print (string function)". It will take the first matching function it finds(searching from the end, so more recently declared functions will take precedence over early declared functions). It seems to take return values into account, however this seems not to work reliably. If it does not find a matching function in the local symbol table, only then it will go to the global DXL context and look there for a function. if there is nothing it will tell you "undeclared variable: print" and quit with a 'compile' error.

I hope I am correct on this one, regards, Mathias



 

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

 

 

Re: The = Operator Function
SystemAdmin - Fri Jan 14 07:00:37 EST 2011

Mathias Mamsch - Wed Jan 12 16:04:42 EST 2011

The following works fine:
 

<costruct MyType {}
 
string GetSomeProperty (MyType) { return dxlHere[10:] "OK" }
MyType ::. (MyType MyTypeObject, string Value) { return MyTypeObject }
MyType ::. (MyType MyTypeObject, int Value) { return MyTypeObject }
 
MyType Test = addr_ (new DxlObject) 
 
print (GetSomeProperty(Test."One")) "\n"
print (GetSomeProperty(Test."One"."Two")) "\n"
print (GetSomeProperty(Test.1)) "\n"
print (GetSomeProperty(Test."One".1)) "\n"
print (GetSomeProperty(Test."One".2)) "\n"
print (GetSomeProperty(Test . 1 . 2)) "\n"
print (GetSomeProperty(Test . 2 . "One")) "\n"

 


The DXL expression evaluation mechanism is pretty complex to understand. DOORS uses yacc with the DXL grammar. Yacc precedence works as followed:

 

 

  • Each terminal (token in the source token stream, e.g. the operators) has a precedence and an associativity (left or right).
  • Grammar rules are evaluated in order of their precedence, which is defined by the right-most terminal of the grammar rule (guess why DXL is right associative?).


Lets have a look at a part of the DXL grammar(look at doors.exe to find it):

 

 

item : range
range : statement
range : expq
expq : exp
expq : exp QUEST(?) exp COLON(:) exp
exp : UMIN(-) exp
exp : PLUS(+) exp
exp : DOLL($) exp3
exp : LNEG(!) exp
exp : ASTX(*) K_CONST_o exp
exp : BAND(&) K_CONST_o exp
exp : exp FSL(/) exp
exp : exp EXP(^^) exp
exp : exp ASTX(*) exp
exp : exp BSL(\) exp
exp : exp REM(%) exp
exp : exp PLUS(+) exp
exp : exp UMIN(-) exp
exp : exp LSHFT(<<) exp
exp : exp RSHFT(>>) exp
exp : exp DIAM() exp
exp : exp MINI(<?) exp
exp : exp MAXI(>?) exp
exp : exp LT(<) exp
exp : exp GT(>) exp
exp : exp LTE(<=) exp
exp : exp GTE(>=) exp
exp : exp EQUS(==) exp
exp : exp NEQUS(!=) exp
exp : exp BAND(&) exp
exp : exp XOR(^) exp
exp : exp BOR(|) exp
exp : exp AND(&&) exp
exp : exp OR(||) exp
exp : exp ASSR(<-) exp
exp : exp ASSP(:=) exp
exp : exp GUARD(=>) exp
 
exp1 : name
exp1 : INT
exp1 : REAL
exp1 : CHARACTER
exp1 : STRING
exp1 : LB simpleType_o rrange_o_range_o RB
 
exp1a : exp1
exp1a : exp1a LSB bounds RSB
 
exp2 : exp1a
exp2 : exp2 SUB(->) exp1a
exp2 : exp2 LINKA(<->) exp1a
exp2 : exp2 DOT(.) exp1a
exp2 : exp2 AT(@) exp1a
exp2 : exp2 PLPL(++)
exp2 : exp2 MINMIN(--)
exp2 : PLPL exp2
exp2 : MINMIN exp2
exp2 : TILDA(~) exp2
exp2 : K_SIZE exp3
exp2 : K_DEFAULT COLON_o rblock
exp3 : exp1
exp3 : eargs
exp4 : exp2
exp4 : exp2 exp4
exp4 : exp2 simpleType
exp4 : exp4 CC exp2
exp : exp4



If I write a . b + C this will be tokenized to name(a), DOT, name(b), PLUS, name(c) . Yacc will take these input tokens, look on the next token and try to evaluate the above rules on it. When no rules apply then yacc will shift the next input token and try again to find a rule. So yacc will start with the input token "name(a)" and apply the following rule: "exp1 : name". So our "name" token becomes a "exp1" token. Yacc will evaluate further: name -> exp1 -> exp2, then it will find, there is a rule "exp2 . exp1a", so it will shift the next input token 'DOT'. Again there is no rule for "exp2 DOT" so take another input token. No we have "exp2 DOT name(b)". Since there is no rule for "exp2 DOT name" it will try to find rules for "name" and continue evaluating:

exp2 DOT name(b) -> exp2 DOT exp1 -> exp2 DOT exp1a -> boom exp2

Here we have found a rule: exp2 : exp2 DOT exp1a, that means the DOT is 'consumed' and therefore executed before the \+ is considered. Would there be no rule for "exp2 DOT something", then DXL would shift the next input token and try to evaluate that. So in this case I can see from the grammar that DXL will evaluate (a.b) + C, so in this case the . operator comes first. So therefore we can see the following.

The operators ->, <->, . ,@ , ++, --, ~ should take precedence over the other operators. Then should come the CC (concatenation) operator. Then comes the rest of the operators. The precedence for the other operators is unclear, we all know that for example * will take precedence over +. One would need to try this.
Lets come to the symbol lookup:

If you call a function in DXL other grammar rules (not listed) apply. First DXL has to differentiate between statements like int a and print a. For this it has internal symbol tables which tell it "print" is a function and "int" is a type and a is variable of type string (for example). Whenever you declare a variable or function it will be appended to the symbol table. Now due to polymorphism the parser needs to determine WHICH print function to call. To be exact every DXL context has its own symbol table. The internal perms are on the top level DXL context while your user program runs in its own context. So if your user program calls print (string) the DXL parser will first look in the local symbol table for a "print (string function)". It will take the first matching function it finds(searching from the end, so more recently declared functions will take precedence over early declared functions). It seems to take return values into account, however this seems not to work reliably. If it does not find a matching function in the local symbol table, only then it will go to the global DXL context and look there for a function. if there is nothing it will tell you "undeclared variable: print" and quit with a 'compile' error.

I hope I am correct on this one, regards, Mathias



 

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

 

 

I understand whats going on under the hood now. Thanks for the details.